﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace CsharpTool.Common
{
    public interface IPerformanceService
    {
        /// <summary>
        /// 获取进程内存使用率
        /// </summary>
        /// <returns>item1: 内存使用量(K); item2: 内存使用百分比</returns>
        Task<(long, decimal)> StatsProcessMemoryUsageAsync(int processId);

        /// <summary>
        /// 获取进程CPU内存使用率
        /// </summary>
        Task<decimal> StatsProcessCPUUsageAsync(int processId);

        /// <summary>
        /// 获取内存使用率
        /// </summary>
        /// <returns>item1: 内存使用量(K); item2: 内存使用百分比</returns>
        Task<(long, decimal)> StatsMemoryUsageAsync();

        /// <summary>
        /// 获取CPU内存使用率
        /// </summary>
        Task<decimal> StatsCPUUsageAsync();

        /// <summary>
        /// 获取系统分区信息
        /// </summary>
        /// <returns>Item1: 分区名称; Item2:总量; Item3:剩余量</returns>
        Task<List<(string, decimal, decimal)>> StatsDiskAsync();
    }

    public class WinPerformanceService : IPerformanceService
    {
        public async Task<decimal> StatsProcessCPUUsageAsync(int processId)
        {
            var cpuCount = GetPhysicalCpuCount();
            var cpu1 = GetMemOrCpu(processId, "cpu");
            await Task.Delay(1000);
            var cpu2 = GetMemOrCpu(processId, "cpu");
            return Math.Round((cpu2 - cpu1) / cpuCount, 2);
        }

        public Task<(long, decimal)> StatsProcessMemoryUsageAsync(int processId)
        {
            var use = GetMemOrCpu(processId, "mem");
            var total = GetTotalPhysicalMemory();
            var percent = Math.Round((use * 1024) / (total == 0 ? 1L : total), 2);
            return Task.FromResult(((long)use, percent));
        }

        public Task<(long, decimal)> StatsMemoryUsageAsync()
        {
            try
            {
#pragma warning disable CA1416 // 验证平台兼容性
                using var memCounter = new PerformanceCounter("Memory", "% Committed Bytes In Use");
                var value = memCounter.NextValue();
#pragma warning restore CA1416 // 验证平台兼容性
                return Task.FromResult((0L, (decimal)Math.Round(value, 2)));
            }
            catch { }

            return Task.FromResult((0L, 0M));
        }

        public async Task<decimal> StatsCPUUsageAsync()
        {
            try
            {
#pragma warning disable CA1416 // 验证平台兼容性
                using (var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"))
                {
                    cpuCounter.NextValue();
                    await Task.Delay(1000);
                    var cpuUsage = cpuCounter.NextValue();
                    return (decimal)Math.Round(cpuUsage, 2);
                }
#pragma warning restore CA1416 // 验证平台兼容性
            }
            catch { }

            return 0m;

        }

        public Task<List<(string, decimal, decimal)>> StatsDiskAsync()
        {
            var result = new List<(string, decimal, decimal)>();
            try
            {
                var drives = DriveInfo.GetDrives();
                foreach (var drive in drives)
                {
                    if (drive.DriveType == DriveType.Fixed)
                    {
                        result.Add((
                            drive.Name,
                            Math.Round((1.0M * drive.TotalSize / 1024 / 1024 / 1024), 2),
                            Math.Round((1.0M * drive.AvailableFreeSpace / 1024 / 1024 / 1024), 2)));
                    }
                }
            }
            catch { }

            return Task.FromResult(result);
        }

        #region private methods
        /// <summary>
        /// 获取程序内存或cpu使用情况
        /// </summary>
        /// <param name="processId">程序进程ID</param>
        /// <param name="type">cpu、mem</param>
        private decimal GetMemOrCpu(int processId, string type = "cpu")
        {
            try
            {
                var cmd = $"Get-Process | Where-Object {{ $_.Id -eq {processId} }}";
                (var output, var error) = ProcessTool.ProcessWinPowerShellCmd(cmd);
                if (!string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(output))
                    return 0m;
                var data = output.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)[2];
                var splits = Regex.Split(data, @"\s+").ToList();
                if (string.IsNullOrWhiteSpace(splits[0]))
                    splits.RemoveAt(0);
                if (type == "cpu")
                    return decimal.Parse(splits[4]);
                else
                    return decimal.Parse(splits[3]);
            }
            catch
            {
                return 0m;
            }
        }

        private long GetTotalPhysicalMemory()
        {
            var cmd = "Get-WmiObject -Class Win32_PhysicalMemory | Select-Object Capacity";
            var total = 0L;
            try
            {
                (var output, var error) = ProcessTool.ProcessWinPowerShellCmd(cmd);
                if (!string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(output))
                    return total;
                var splits = output.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
                for (var i = 2; i < splits.Length; i++)
                {
                    total += long.Parse(splits[i]);
                }

                return total;
            }
            catch
            {
                return total;
            }
        }

        private int GetPhysicalCpuCount()
        {
            var cmd = "Get-WmiObject -Class Win32_Processor | Select-Object NumberOfCores";
            var total = 1;
            try
            {
                (var output, var error) = ProcessTool.ProcessWinPowerShellCmd(cmd);
                if (!string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(output))
                    return total;
                var splits = output.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
                total = int.Parse(splits[2]);
                return total;
            }
            catch { return total; }
        }

        #endregion
    }

    /// <summary>
    /// LinuxPerformanceService
    /// </summary>
    public class LinuxPerformanceService : IPerformanceService
    {
        public Task<decimal> StatsProcessCPUUsageAsync(int processId)
        {
            var useCpu = GetMemOrCpu(processId, "cpu");
			return Task.FromResult(useCpu / GetCpuCount());
        }

        public Task<(long, decimal)> StatsProcessMemoryUsageAsync(int processId)
        {
            var percent = GetMemOrCpu(processId, "mem1");
            var use = (long)GetMemOrCpu(processId, "mem2");
            return Task.FromResult((use, percent));
        }

        public Task<(long, decimal)> StatsMemoryUsageAsync()
        {
            var data = "";
            try
            {
                (var output, var error) = ProcessTool.ProcessLinuxCmd("free -m");
                if (!string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(output))
                    return Task.FromResult((0L, 0M));
                foreach (var line in output.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries))
                {
                    var l = line.Trim();
                    if (l.StartsWith("Mem:"))
                    {
                        data = l;
                        var values = Regex.Split(l, @"\s+");
                        var total = decimal.Parse(values[1]);
                        var available = decimal.Parse(values[6]);
                        return Task.FromResult(((long)(total - available), Math.Round(((total - available) / total), 2)));
                    }
                }
            }
            catch (Exception e)
            {
                //_logger.LogError(e, $"获取Linux内存使用率失败: {data}->{e.Message}");
            }

            return Task.FromResult((0L, 0M));
        }

        public Task<decimal> StatsCPUUsageAsync()
        {
            var data = "";
            try
            {
                (var output, var error) = ProcessTool.ProcessLinuxCmd("top -b -n 1");
                if (!string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(output))
                    return Task.FromResult(0M);
                foreach (var line in output.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries))
                {
                    var l = line.Trim();
                    if (l.StartsWith("%Cpu(s):"))
                    {
                        data = l;
                        var values = Regex.Split(l, @",");
                        var idle = decimal.Parse(values[3].Replace("id", "").Trim());
                        return Task.FromResult(Math.Round((100m - idle), 2));
                    }
                }

            }
            catch (Exception e)
            {
                //_logger.LogError(e, $"获取Linux CPU使用率失败: {data} -> {e.Message}");
            }

            return Task.FromResult(0M);
        }

        public Task<List<(string, decimal, decimal)>> StatsDiskAsync()
        {
            var result = new List<(string, decimal, decimal)>();

            try
            {
                (var output, var error) = ProcessTool.ProcessLinuxCmd("df -BM | grep /dev/ | grep -v /loop");
                if (!string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(output))
                    return Task.FromResult(result);
                foreach (var line in output.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries))
                {
                    var l = line.Trim();
                    if (l.StartsWith("/dev/"))
                    {
                        var values = Regex.Split(l, @"\s+");
                        result.Add((
                            $"{values[0]}({values[5]})",
                            Math.Round((decimal.Parse(values[1].Replace("M", "")) / 1024), 2),
                            Math.Round(decimal.Parse(values[3].Replace("M", "")) / 1024, 2)
                        ));
                    }
                }
            }
            catch (Exception e)
            {
                // _logger.LogError(e, $"获取Linux硬盘使用情况失败: {e.Message}");
            }

            return Task.FromResult(result);
        }

        #region private methods
        /// <summary>
        /// 获取程序内存或cpu使用率
        /// </summary>
        /// <param name="processId">程序进程ID</param>
        /// <param name="type">cpu、mem1(百分比)、mem2(使用量)</param>
        private decimal GetMemOrCpu(int processId, string type = "cpu")
        {
            try
            {
                (var output, var error) = ProcessTool.ProcessLinuxCmd($"top -b -n 1 -p {processId}");
                if (!string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(output))
                    return 0m;
                var data = output.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Last();
                var splits = Regex.Split(data, @"\s+").ToList();
                if (string.IsNullOrWhiteSpace(splits[0]))
                    splits.RemoveAt(0);
                if (splits[0] != $"{processId}")
                    return 0m;
                if (type == "cpu")
                    return Math.Round(decimal.Parse(splits[8]), 2);
                else if (type == "mem1")
                    return Math.Round(decimal.Parse(splits[9]), 2);
                else
                    return Math.Round(decimal.Parse(splits[5]), 2);

            }
            catch
            {
                return 0m;
            }
        }
        
        /// <summary>
		/// 获取cpu核心数量
		/// </summary>
		private int GetCpuCount()
		{
			try
			{
				(var output, var error) = CmdTool.ProcessLinuxCmd("cat /proc/cpuinfo | grep processor | wc -l");
				if (!string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(output))
					return 1;
				return int.Parse(output.Trim());
			}
			catch
			{
				return 1;
			}
		}
        #endregion
    }
}
